/*
 *  pixels.h
 *  
 *
 *  Created by Jason Hsu on 2/8/11.
 *	
 */

#include <stdlib.h>
#include <tuple3.h>
#include <Color.h>
#include <texture.h>
#include <math.h>

class Pixels
{
private:
	const static GLint NUM_SUB_PIXELS = 4;				//number of sub-Pixels created upon splitting
	const static GLdouble FADE_TO_WHITE_SPEED = 0.06;	//how quickly the Pixel fades to white
	
	GLdouble mySideLength;			//side length of this particular Pixel
	Color* myColor;					//color of Pixel
	Point3* myPosition;				//position of top left corner
	Pixels* mySubPixels[NUM_SUB_PIXELS];	//holds sub-Pixels
	bool myPixelOn;					//switch to turn Pixel on or off (whether or not this Pixel should be drawn)
	GLint myLevel;					//what level the Pixel is at (how many times the original pixel has split)
	
	//GLUquadric* quad;				//remnant from when I wanted to do circles instead of squares
	
public:
	
	/*
	 * Default constructor. Generates random colors for Pixels, and points are (0,0,0).
	 */
	Pixels()
	{
		mySideLength = 0;
		myColor = new Color((GLdouble)rand()/RAND_MAX, (GLdouble)rand()/RAND_MAX, (GLdouble)rand()/RAND_MAX, 0.0);
		myPosition = new Point3();
		myPixelOn = true;
		myLevel = 0;
		//quad = gluNewQuadric();
	}
	
	/*
	 * Destructor. Deletes sub-Pixels when appropriate.
	 */
	~Pixels()
	{
		delete myColor;
		delete myPosition;
		if(!myPixelOn)	//if this Pixel is "off," it means sub-Pixels were created
		{
			for(GLint k = 0; k<NUM_SUB_PIXELS; k++)
			{
				delete mySubPixels[k];
			}
		}
	}
	
	/*
	 * Explicit constructor. I never use it...why did I write it?
	 */
	Pixels(GLdouble sideLength, Color* color, Point3* position, bool pixelOn, GLint level)
	{
		mySideLength = sideLength;
		myColor = new Color(color->r, color->g, color->b, color->a);
		myPosition = new Point3(position->x, position->y, position->z);
		myPixelOn = pixelOn;
		myLevel = level;
		//quad = gluNewQuadric();
	}
	
	void setColor(Color* color)
	{
		myColor->r = color->r;
		myColor->g = color->g;
		myColor->b = color->b;
		myColor->a = color->a;
	}
	
	/*
	 * Sets the color of this Pixel according to the texture given. Scans texture and
	 * averages the colors of all the pixels in the actual image corresponding to the
	 * area covered by this Pixel. That average color is set as the color of this Pixel.
	 *
	 * Texture* texture is the texture that governs what color this Pixel should be.
	 */
	void setColor(Texture* texture)
	{
		if(myPixelOn)
		{
			GLubyte* imageData = texture->getImageData();
			GLuint width = texture->getImageWidth();
			GLuint depth = texture->getImageDepth();
			
			GLdouble redSum = 0;
			GLdouble greenSum = 0;
			GLdouble blueSum = 0;
			
			//flooring because side length eventually becomes decimal value
			for(GLint r = 0; r<floor(mySideLength); r++)
			{
				for(GLint c = 0; c<floor(mySideLength); c++)
				{
					GLint index = ((floor(myPosition->x) + c) + (-floor(myPosition->y) + r)*width)*depth;
					redSum += imageData[index];
					greenSum += imageData[index+1];
					blueSum += imageData[index+2];
				}
			}
			GLdouble sideSquared = floor(mySideLength)*floor(mySideLength);
			
			GLdouble redAvg = redSum/sideSquared/256;
			GLdouble greenAvg = greenSum/sideSquared/256;
			GLdouble blueAvg = blueSum/sideSquared/256;
						
			myColor->r = redAvg;
			myColor->g = greenAvg;
			myColor->b = blueAvg;
		}
		else	//if Pixel is off, then need to set colors of its sub-Pixels, not itself
		{
			for(GLint k = 0; k<NUM_SUB_PIXELS; k++)
			{
				mySubPixels[k]->setColor(texture);
			}
		}

	}
	
	void setSideLength(GLdouble sideLength)
	{
		mySideLength = sideLength;
	}
		
	void setPosition(GLdouble x, GLdouble y, GLdouble z)
	{
		myPosition->x = x;
		myPosition->y = y;
		myPosition->z = z;
	}
	
	void setPosition(Point3* position)
	{
		myPosition->x = position->x;
		myPosition->y = position->y;
		myPosition->z = position->z;
	}
	
	void setPixelOnOff(bool flag)	//toggle seems less clear, so went with explicit set
	{
		myPixelOn = flag;
	}
	
	void setLevel(GLint level)
	{
		myLevel = level;
	}
	
	/*
	 * Moves this Pixel into position and draws it onto the screen.
	 */
	void draw()
	{
		if(myPixelOn)	//if Pixel is on, draw itself
		{
			glPushMatrix();
			
			glColor3d(myColor->r, myColor->g, myColor->b);
			glTranslated(myPosition->x, myPosition->y, myPosition->z);
			glBegin(GL_QUADS);	//front-facing squares
				glVertex3d(0.0, 0.0, 0.0);
				glVertex3d(0.0, -mySideLength, 0.0);
				glVertex3d(mySideLength, -mySideLength, 0.0);
				glVertex3d(mySideLength, 0.0, 0.0);
			glEnd();
			
			glPopMatrix();
		}
		else	//if Pixel is off, should draw sub-Pixels instead
		{
			for(int k = 0; k<NUM_SUB_PIXELS; k++)
			{
				mySubPixels[k]->draw();
			}
		}

	}
	
	/*
	 * Splits this Pixel into four sub-Pixels.
	 * If Pixel can still split further, initialize the four sub-Pixels with corresponding
	 * side lengths, levels, and positions, and then turn off this Pixel.
	 */
	void split(GLint maxLevels)
	{
		if(myLevel < maxLevels)
		{
			if(myPixelOn)
			{
				GLdouble newSideLength = mySideLength/2.0;		//only applicable when four sub-pixels
				
				//there is probably a better way to do this
				for(int k = 0; k<NUM_SUB_PIXELS; k++)
				{
					mySubPixels[k] = new Pixels();
					mySubPixels[k]->setSideLength(newSideLength);
					mySubPixels[k]->setLevel(myLevel + 1);
				}
				GLdouble newXCoord = myPosition->x + newSideLength;
				GLdouble newYCoord = myPosition->y - newSideLength;
				mySubPixels[0]->setPosition(myPosition);
				mySubPixels[1]->setPosition(newXCoord, myPosition->y, myPosition->z);
				mySubPixels[2]->setPosition(newXCoord, newYCoord, myPosition->z);
				mySubPixels[3]->setPosition(myPosition->x, newYCoord, myPosition->z);
				
				myPixelOn = false;
			}
			else 
			{
				//this will never occur...i think
			}
		}
	}
	
	/*
	 * Gradually fades this Pixel to a white color. Occurs when something (blob or mouse pointer)
	 * passes over this Pixel.
	 *
	 * returns:
	 *	true if completely white (signals Pixel to split)
	 *	false if not yet completely white
	 *	2 if Pixel is not on
	 */
	GLint fadeToWhite(GLint maxLevels)
	{
		if(myLevel < maxLevels)
		{
			if(myPixelOn)
			{
				if(myColor->r < 1 || myColor->g < 1 || myColor->b < 1)
				{
					myColor->r += FADE_TO_WHITE_SPEED;
					myColor->g += FADE_TO_WHITE_SPEED;
					myColor->b += FADE_TO_WHITE_SPEED;
					return false;
				}
				else 
				{
					return true;
				}
			}
			else 
			{
				return 2;
				//this will never occur...i think
			}
		}
		else
		{
			return 2;
		}
	}
	
	/*
	 * Maps the given screen coordinate to the corresponding Pixel on the screen.
	 *
	 * GLint x, y are screen coordinates (y is positive going down)
	 */
	Pixels* getPixelFromScreenCoordinates(GLint x, GLint y)
	{
		if(myPixelOn)	//if this Pixel is on, then coordinate points to this Pixel
		{
			return this;
		}
		else	//if this Pixel is off, figure out which quadrant coordinate is in, and perform this check on that sub-Pixel
		{
			GLdouble xMidpoint = myPosition->x + mySideLength/2;	//used to differentiate left or right sub-Pixels
			GLdouble yMidpoint = myPosition->y - mySideLength/2;	//used to differentiate top or bottom sub-Pixels
			
			/*
			 * the y's below are negative because the screen's y-coordinates are positive going down
			 * while the Pixel position's y-coordinates (world coordinates) are negative going down
			 */
			if(x < xMidpoint && -y > yMidpoint)
			{
				return mySubPixels[0]->getPixelFromScreenCoordinates(x, y);
			}
			else if(x > xMidpoint && -y > yMidpoint)
			{
				return mySubPixels[1]->getPixelFromScreenCoordinates(x, y);
			}
			else if(x > xMidpoint && -y < yMidpoint)
			{
				return mySubPixels[2]->getPixelFromScreenCoordinates(x, y);
			}
			else
			{
				return mySubPixels[3]->getPixelFromScreenCoordinates(x, y);
			}
		}
	}
};